package chc

import (
	"errors"
	"fmt"
	"reflect"
)

func getMiInd(md interface{}, needPtr bool) (mi *modelInfo, ind reflect.Value) {
	val := reflect.ValueOf(md)
	ind = reflect.Indirect(val)
	typ := ind.Type()
	if needPtr && val.Kind() != reflect.Ptr {
		panic(fmt.Errorf("<Ormer> cannot use non-ptr model struct `%s`", getFullName(typ)))
	}
	name := getFullName(typ)
	if mi, ok := modelCache.getByFullName(name); ok {
		return mi, ind
	}
	panic(fmt.Errorf("<Ormer> table: `%s` not found, make sure it was registered with `RegisterModel()`", name))
}

func crudInsert(md interface{}) error {
	mi, ind := getMiInd(md, true)
	return dbInsert(mi, ind, DefaultTimeLoc)
}

func crudInsertMulti(mds interface{}) error {
	sind := reflect.Indirect(reflect.ValueOf(mds))

	switch sind.Kind() {
	case reflect.Array, reflect.Slice:
		if sind.Len() == 0 {
			return ErrArgs
		}
	default:
		return ErrArgs
	}

	var (
		names  []string
		values []interface{}
		mi     *modelInfo
	)

	for i := 0; i < sind.Len(); i++ {
		ind := reflect.Indirect(sind.Index(i))
		mi, _ = getMiInd(ind.Interface(), false)

		ns := make([]string, 0, len(mi.fields.dbcols))
		vs, err := collectValues(mi, ind, mi.fields.dbcols, false, true, &ns, DefaultTimeLoc)
		if nil != err {
			return errors.New("TODO")
		}

		names = ns
		values = append(values, vs...)
	}

	return dbInsertValue(mi, names, values)
}

func crudDelete(md interface{}, cols ...string) (int64, error) {
	mi, ind := getMiInd(md, true)
	return dbDelete(mi, ind, DefaultTimeLoc, cols)
}

func crudUpdate(md interface{}, cols ...string) (int64, error) {
	mi, ind := getMiInd(md, true)
	return dbUpdate(mi, ind, DefaultTimeLoc, cols)
}

func crudRead(md interface{}, cols ...string) error {
	mi, ind := getMiInd(md, true)
	return dbRead(mi, ind, DefaultTimeLoc, cols)
}

func crudReadBatch(md interface{}, container interface{}, cols ...string) (int64, error) {
	qs := QueryTable(md)
	return dbReadBatch(qs, qs.mi, qs.cond, container, DefaultTimeLoc, cols)
}
